# -*- coding: utf-8 -*-
"""
视频渲染步骤
将动画渲染为最终视频
"""

import json
import logging
import subprocess
from pathlib import Path
from typing import Dict, Any, List, Optional

from ..step_base import BaseStep, StepResult

logger = logging.getLogger(__name__)


class RenderVideoStep(BaseStep):
    """视频渲染步骤"""

    name = "render_video"
    description = "渲染最终视频"
    required_inputs = ["main_scene_file"]
    optional_inputs = ["audio_files", "output_settings"]
    produces_outputs = ["video_files"]

    def __init__(
        self,
        output_dir: Path,
        main_scene_file: Optional[Path] = None,
        audio_files: Optional[List[Path]] = None,
        output_settings: Optional[Dict[str, Any]] = None,
        **kwargs,
    ):
        """
        初始化步骤

        Args:
            output_dir: 输出目录
            main_scene_file: 主场景文件
            audio_files: 音频文件列表
            output_settings: 输出设置
        """
        super().__init__(output_dir, **kwargs)
        self.main_scene_file = main_scene_file
        self.audio_files = audio_files or []
        self.output_settings = output_settings or {
            "format": "mp4",
            "quality": "standard",
            "resolution": "1920x1080",
            "framerate": 30,
        }

        # 检查ffmpeg
        self.has_ffmpeg = self._check_ffmpeg()

    def validate_inputs(self) -> tuple[bool, list[str]]:
        """验证输入"""
        errors = []

        if not self.main_scene_file:
            errors.append("No main scene file provided")
        elif not self.main_scene_file.exists():
            errors.append(f"Main scene file not found: {self.main_scene_file}")

        if self.output_settings["format"] not in ["mp4", "mov", "avi"]:
            errors.append(f"Unsupported output format: {self.output_settings['format']}")

        return len(errors) == 0, errors

    def run(self) -> StepResult:
        """执行步骤"""
        try:
            self.report_progress(0, "Starting video rendering")

            # 步骤1: 渲染动画
            self.report_progress(30, "Rendering animations")

            video_file = self._render_animations()

            # 步骤2: 合并音频（如果有）
            if self.audio_files:
                self.report_progress(60, "Merging audio")

                final_video = self._merge_audio(video_file)
            else:
                final_video = video_file

            # 步骤3: 添加字幕（如果有）
            if hasattr(self, "subtitle_file") and self.subtitle_file:
                self.report_progress(80, "Adding subtitles")

                final_video = self._add_subtitles(final_video)

            # 完成
            self.report_progress(100, f"Video rendered successfully: {final_video.name}")

            return StepResult(
                success=True,
                message="Video rendered successfully",
                data={
                    "video_file": str(final_video),
                    "format": self.output_settings["format"],
                    "resolution": self.output_settings["resolution"],
                },
                output_files=[final_video],
            )

        except Exception as e:
            logger.exception("Failed to render video")
            return StepResult(
                success=False,
                message=f"Failed to render video: {str(e)}",
            )

    def _render_animations(self) -> Path:
        """
        渲染动画

        Returns:
            视频文件路径
        """
        # 检查Manim是否安装
        if not self._check_manim():
            raise RuntimeError("Manim is not installed or not in PATH")

        # 构建Manim命令
        cmd = self._build_manim_command()

        logger.info(f"Running Manim: {' '.join(cmd)}")

        # 执行渲染
        try:
            result = subprocess.run(
                cmd,
                cwd=self.output_dir,
                capture_output=True,
                text=True,
                timeout=3600,  # 1小时超时
            )

            if result.returncode != 0:
                raise RuntimeError(f"Manim rendering failed: {result.stderr}")

            # 查找生成的视频文件
            output_pattern = self.output_settings["format"]
            video_files = list(self.output_dir.glob(f"**/*.{output_pattern}"))

            if not video_files:
                raise RuntimeError("No video file generated")

            # 返回第一个视频文件
            return video_files[0]

        except subprocess.TimeoutExpired:
            raise RuntimeError("Manim rendering timed out")

    def _build_manim_command(self) -> List[str]:
        """
        构建Manim命令

        Returns:
            命令列表
        """
        cmd = ["manim"]

        # 质量设置
        quality_map = {
            "low": "l",
            "standard": "m",
            "high": "h",
            "ultra": "k",
        }
        quality = self.output_settings.get("quality", "standard")
        cmd.append(f"-{quality_map.get(quality, 'm')}")

        # 输出目录
        cmd.extend(["--output_file", str(self.output_dir)])

        # 主场景文件
        cmd.append(str(self.main_scene_file))

        # 场景类名（如果指定）
        if "scene_class" in self.output_settings:
            cmd.append(self.output_settings["scene_class"])

        return cmd

    def _merge_audio(self, video_file: Path) -> Path:
        """
        合并音频

        Args:
            video_file: 视频文件

        Returns:
            合并后的视频文件
        """
        if not self.has_ffmpeg:
            logger.warning("FFmpeg not found, skipping audio merge")
            return video_file

        # 创建输出文件
        output_file = self.output_dir / f"with_audio_{video_file.name}"

        # 构建ffmpeg命令
        cmd = ["ffmpeg", "-y", "-i", str(video_file)]

        # 添加音频文件
        for audio_file in self.audio_files:
            cmd.extend(["-i", str(audio_file)])

        # 合并策略：使用第一个音频文件
        cmd.extend([
            "-c:v", "copy",  # 复制视频流
            "-c:a", "aac",   # 音频编码
            "-map", "0:v:0", # 映射第一个输入的视频流
            "-map", "1:a:0", # 映射第二个输入的音频流
            "-shortest",     # 以最短的流为准
            str(output_file),
        ])

        logger.info(f"Running FFmpeg: {' '.join(cmd)}")

        try:
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=300,  # 5分钟超时
            )

            if result.returncode != 0:
                raise RuntimeError(f"FFmpeg failed: {result.stderr}")

            return output_file

        except subprocess.TimeoutExpired:
            raise RuntimeError("FFmpeg timed out")

    def _add_subtitles(self, video_file: Path) -> Path:
        """
        添加字幕

        Args:
            video_file: 视频文件

        Returns:
            带字幕的视频文件
        """
        if not self.has_ffmpeg:
            logger.warning("FFmpeg not found, skipping subtitle merge")
            return video_file

        # 创建输出文件
        output_file = self.output_dir / f"with_subtitles_{video_file.name}"

        # 构建ffmpeg命令
        cmd = [
            "ffmpeg", "-y",
            "-i", str(video_file),
            "-vf", f"subtitles={self.subtitle_file}",
            "-c:a", "copy",
            str(output_file),
        ]

        logger.info(f"Running FFmpeg: {' '.join(cmd)}")

        try:
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=300,
            )

            if result.returncode != 0:
                raise RuntimeError(f"FFmpeg failed: {result.stderr}")

            return output_file

        except subprocess.TimeoutExpired:
            raise RuntimeError("FFmpeg timed out")

    def _check_manim(self) -> bool:
        """
        检查Manim是否可用

        Returns:
            是否可用
        """
        try:
            result = subprocess.run(
                ["manim", "--version"],
                capture_output=True,
                text=True,
                timeout=5,
            )
            return result.returncode == 0
        except (subprocess.TimeoutExpired, FileNotFoundError):
            return False

    def _check_ffmpeg(self) -> bool:
        """
        检查FFmpeg是否可用

        Returns:
            是否可用
        """
        try:
            result = subprocess.run(
                ["ffmpeg", "-version"],
                capture_output=True,
                text=True,
                timeout=5,
            )
            return result.returncode == 0
        except (subprocess.TimeoutExpired, FileNotFoundError):
            return False
